/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.feature.solr.local;

import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.DisMaxParams;
import yhao.infra.feature.solr.local.core.facet.FacetQueryInfo;
import yhao.infra.feature.solr.local.core.facet.FacetQueryParam;
import yhao.infra.feature.solr.local.core.facet.SolrFacetBean;
import yhao.infra.feature.solr.local.core.group.GroupQueryInfo;
import yhao.infra.feature.solr.local.core.group.SolrGroupBean;
import yhao.infra.feature.solr.local.core.stats.StatsQueryInfo;

import java.util.ArrayList;
import java.util.List;

/**
 * solr 查询条件包装类
 *
 * @author yoara
 */
public class SearchCriteria{
	private String queryType;// 查询类型
	private String defType;// 默认查询类型
	private String fl;// 返回字段列表
	private String pf;// edismax属性，匹配字段
	private String qf;// edismax属性，查询字段（同于df）以及设置权重
	private String bf;// edismax属性，函数权重
	private GeoDistParam geodistWith;//使用geodist()函数

	//group查询
	private GroupQueryInfo groupQueryInfo;
	//facet查询
	private FacetQueryInfo facetQueryInfo;
	//stat查询
	private StatsQueryInfo statsQueryInfo;
	//高亮字段
	private List<String> highlightFields;

	//拼装q查询参数的StringBuffer
	private StringBuffer query = new StringBuffer();

	/** 新增q 查询参数 **/
	public void addQuery(FieldValuePair pair,OpeType opeType){
		addQuery(pair.field + ":" + pair.value,opeType);
	}

	/** 新增多个关联q 查询参数 **/
	public void addQuerys(List<FieldValuePair> pairs,OpeType innerOpeType,OpeType opeType){
		StringBuffer sb = new StringBuffer("(");
		for(FieldValuePair pair:pairs){
			if(sb.length()>1){
				sb.append(" ").append(innerOpeType).append(" ");
			}
			sb.append(pair.field).append(":").append(pair.value);
		}
		sb.append(")");
		addQuery(sb.toString(),opeType);
	}

	static public class FieldValuePair{
		private String field;
		private String value;

		public FieldValuePair(String field, String value) {
			this.field = field;
			this.value = value;
		}
	}

	/** 新增q 查询参数 **/
	public void addRangeQuery(String field,String start,String end,OpeType opeType){
		StringBuffer range = new StringBuffer();
		range.append("[").append(start).append(" TO ").append(end).append("]");
		addQuery(field + ":" + range.toString(),opeType);
	}

	/** 地址查询
	 * value格式：纬度,经度
	 * distance：半径公里
	 * **/
	public void addLocationQuery(GeoDistParam param,OpeType opeType){
		param.opeType = opeType==null?OpeType.AND:opeType;
		this.geodistWith = param;
	}

	/** 新增q 查询参数 **/
	public void addQuery(String qStr,OpeType opeType){
		if(query.length()!=0){
			query.append(" ").append(opeType).append(" ");
		}
		query.append(qStr);
	}

	//拼装fq查询参数的List
	private List<String> filterQuery = new ArrayList<>();
	/** 新增fq 查询参数 **/
	public void addFilterQuery(String field,String value){
		addFilterQuery(field + ":" + value);
	}
	/** 新增fq 查询参数 **/
	public void addFilterQuery(String fqStr){
		filterQuery.add(fqStr);
	}

	//拼装sort的List
	private List<SolrQuery.SortClause> sortList = new ArrayList<>();
	/** 新增sort 参数 **/
	public void addSort(String field,SolrQuery.ORDER order){
		sortList.add(SolrQuery.SortClause.create(field,order));
	}

	/**
	 * 请勿直接调用此方法
	 * @return
	 */
	public SolrQuery makeSolrQuery() {
		SolrQuery solrQuery = new SolrQuery();

		if(customQuery(solrQuery)){
			filterQuery.forEach(solrQuery::addFilterQuery);
			sortList.forEach(solrQuery::addSort);

			if (null != defType) {
				solrQuery.setParam("defType", defType);
			}
			if (null != queryType) {
				solrQuery.setRequestHandler(queryType);
			}

			if (null != pf) {
				solrQuery.setParam(DisMaxParams.PF, pf);
			}

			if (null != qf) {
				solrQuery.setParam(DisMaxParams.QF, qf);
			}

			if (null != bf) {
				solrQuery.setParam(DisMaxParams.BF, bf);
			}

			StringBuffer sb = new StringBuffer();
			if (null != fl) {
				sb.append(fl);
			}else{
				sb.append("*");
			}
			if(geodistWith != null){
				if(geodistWith.needScore){
					sb.append(",").append("score");
				}
				if(StringUtils.isNotBlank(geodistWith.getReturnDistField())){
					sb.append(",").append(geodistWith.getReturnDistField()).append(":geodist()");
				}
			}
			solrQuery.setParam(CommonParams.FL, sb.toString());

			if (geodistWith != null){
				solrQuery.set("pt",geodistWith.getPt());
				solrQuery.set("sfield",geodistWith.getField());
				if(StringUtils.isNotBlank(geodistWith.getD())){
					solrQuery.set("d",geodistWith.getD());
					StringBuffer geoSb = new StringBuffer();
					geoSb.append("{!geofilt");
					if(geodistWith.needScore){
						geoSb.append(" score=distance");
					}
					geoSb.append("}");
					addQuery(geoSb.toString(),geodistWith.getOpeType());
				}
			}

			if (facetQueryInfo != null) {
				facetQueryInfo.makeFacetIndex(solrQuery);
			}
			if (groupQueryInfo!=null) {
				groupQueryInfo.makeGroupIndex(solrQuery);
			}
			if (statsQueryInfo!=null) {
				statsQueryInfo.makeStatsIndex(solrQuery);
			}
			solrQuery.setQuery(query.length()>0?query.toString():"*:*");
		}
		return solrQuery;
	}

	public GroupQueryInfo getGroupQueryInfo() {
		return groupQueryInfo;
	}

	public FacetQueryInfo getFacetQueryInfo() {
		return facetQueryInfo;
	}

	public StatsQueryInfo getStatsQueryInfo() {
		return statsQueryInfo;
	}

	/**
	 * 生成组查询参数
	 * @param groupFields group分组字段
	 * @param groupLimit group每组返回个数，默认返回1个
	 */
	public void setGroupQueryInfo(List<String> groupFields, Integer groupLimit) {
		if(groupFields==null){
			return ;
		}
		List<SolrGroupBean> groupFieldBeanList = new ArrayList<>();
		for(String field:groupFields){
			groupFieldBeanList.add(new SolrGroupBean(field));
		}
		this.groupQueryInfo = new GroupQueryInfo(groupFieldBeanList,groupLimit);
	}

	/**
	 * 生成面查询参数
	 * @param pairs 面聚合字段，无分组字段则传入null
	 */
	public void setFacetQueryInfo(List<FieldValuePair> pairs, FacetQueryParam param) {
		if(pairs==null || pairs.size()==0){
			return;
		}
		List<SolrFacetBean> facetFields = new ArrayList<>();
		for(FieldValuePair pair:pairs){
			facetFields.add(new SolrFacetBean(pair.field,pair.value));
		}
		this.facetQueryInfo = new FacetQueryInfo(facetFields,param);
	}

	/**
	 * 生成统计查询参数
	 * @param fields 统计查询字段，无分组字段则传入null
	 */
	public void setStatsQueryInfo(List<String> fields) {
		if(fields==null || fields.size()==0){
			return;
		}
		this.statsQueryInfo = new StatsQueryInfo(fields.toArray(new String[]{}));
	}

	/**
	 * 返回false则不追加默认的查询封装
	 * @param solrQuery
	 * @return
	 */
	protected boolean customQuery(SolrQuery solrQuery) {
		return true;
	}

	public String getQueryType() {
		return queryType;
	}

	public void setQueryType(String queryType) {
		this.queryType = queryType;
	}

	public String getDefType() {
		return defType;
	}

	public void setDefType(String defType) {
		this.defType = defType;
	}

	public String getFl() {
		return fl;
	}

	public void setFl(String fl) {
		this.fl = fl;
	}

	public String getQf() {
		return qf;
	}

	public void setQf(String qf) {
		this.qf = qf;
	}

	public String getPf() {
		return pf;
	}

	public void setPf(String pf) {
		this.pf = pf;
	}

	public String getBf() {
		return bf;
	}

	public void setBf(String bf) {
		this.bf = bf;
	}

	public void setGeodistWith(GeoDistParam geodistWith) {
		this.geodistWith = geodistWith;
	}

	public List<String> getHighlightFields() {
		return highlightFields;
	}

	public void setHighlightFields(List<String> highlightFields) {
		this.highlightFields = highlightFields;
	}

	public enum OpeType{
		AND,OR,NOT
	}

	static public class GeoDistParam{
		//用于空间查询的字段
		private String field;
		//"lat,lon"组成的字符串
		private String pt;
		//辐射半径，单位为千米
		private String d;
		//是否需要计算积分
		private boolean needScore;
		//返回距离的字段
		private String returnDistField;
		//查询类型
		private OpeType opeType;

		public String getField() {
			return field;
		}

		public String getPt() {
			return pt;
		}

		public GeoDistParam(String field, String pt,String d) {
			this.field = field;
			this.pt = pt;
			this.d = d;
		}

		public OpeType getOpeType() {
			return opeType;
		}

		public String getD() {
			return d;
		}

		public boolean isNeedScore() {
			return needScore;
		}

		public void setNeedScore(boolean needScore) {
			this.needScore = needScore;
		}

		public String getReturnDistField() {
			return returnDistField;
		}

		public GeoDistParam setReturnDistField(String returnDistField) {
			this.returnDistField = returnDistField;
			return this;
		}

		public GeoDistParam setOpeType(OpeType opeType) {
			this.opeType = opeType;
			return this;
		}
	}
}
